package com.starry.module.system.core.code.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.lock.annotation.Lock4j;
import com.starry.core.common.constants.CommonError;
import com.starry.core.common.domain.vo.PageVo;
import com.starry.core.common.execption.ServiceException;
import com.starry.db.core.query.LambdaQueryWrapperX;
import com.starry.module.system.api.code.domain.qo.CodeRuleConfigPageQo;
import com.starry.module.system.api.code.domain.qo.CodeRuleConfigQo;
import com.starry.module.system.api.code.domain.vo.CodeRuleConfigDtlVo;
import com.starry.module.system.api.code.domain.vo.CodeRuleConfigVo;
import com.starry.module.system.core.code.entity.CodeRuleConfig;
import com.starry.module.system.core.code.mapper.CodeRuleConfigMapper;
import com.starry.module.system.core.code.service.CodeRuleConfigService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.UUID;

/**
 * 编码规则配置-服务实现
 * @author xiaoke
 */
@Service("codeRuleConfigService")
public class CodeRuleConfigServiceImpl implements CodeRuleConfigService {

    private final CodeRuleConfigMapper codeRuleConfigMapper;

    public CodeRuleConfigServiceImpl(CodeRuleConfigMapper codeRuleConfigMapper) {
        this.codeRuleConfigMapper = codeRuleConfigMapper;
    }

    @Override
    public PageVo<CodeRuleConfigVo> page(CodeRuleConfigPageQo pageQo) {
        PageVo<CodeRuleConfig> page = codeRuleConfigMapper.selectPage(pageQo);
        List<CodeRuleConfigVo> vos = BeanUtil.copyToList(page.getRows(), CodeRuleConfigVo.class);
        return new PageVo<>(vos, page.getTotal());
    }

    @Override
    public CodeRuleConfigDtlVo getById(Long id) {
        CodeRuleConfig codeRuleConfig = codeRuleConfigMapper.selectById(id);
        if (codeRuleConfig == null) {
            throw new ServiceException(CommonError.DATA_NOT_EXISTS.getMsg());
        }
        return BeanUtil.toBean(codeRuleConfig, CodeRuleConfigDtlVo.class);
    }

    @Transactional
    @Override
    public Long save(CodeRuleConfigQo codeRuleConfigQo) {
        LambdaQueryWrapperX<CodeRuleConfig> wrapper = new LambdaQueryWrapperX<>();
        wrapper.eq(CodeRuleConfig::getRuleKey, codeRuleConfigQo.getRuleKey());
        if (codeRuleConfigMapper.selectCount(wrapper) > 0) {
            throw new ServiceException(CommonError.DATA_EXISTS.getMsg());
        }
        CodeRuleConfig codeRuleConfig = BeanUtil.toBean(codeRuleConfigQo, CodeRuleConfig.class);
        codeRuleConfig.setId(null);
        codeRuleConfigMapper.insert(codeRuleConfig);
        return codeRuleConfig.getId();
    }

    @Transactional
    @Override
    public void updateById(CodeRuleConfigQo codeRuleConfigQo) {
        if (codeRuleConfigQo.getId() == null) {
            throw new ServiceException(CommonError.DATA_NOT_EXISTS.getMsg());
        }
        LambdaQueryWrapperX<CodeRuleConfig> wrapper = new LambdaQueryWrapperX<>();
        wrapper.ne(CodeRuleConfig::getId, codeRuleConfigQo.getId()).and(w -> w.eq(CodeRuleConfig::getRuleKey, codeRuleConfigQo.getRuleKey()));
        if (codeRuleConfigMapper.selectCount(wrapper) > 0) {
            throw new ServiceException(CommonError.DATA_EXISTS.getMsg());
        }
        CodeRuleConfig codeRuleConfig = codeRuleConfigMapper.selectById(codeRuleConfigQo.getId());
        if (codeRuleConfig == null) {
            throw new ServiceException(CommonError.DATA_NOT_EXISTS.getMsg());
        }
        BeanUtil.copyProperties(codeRuleConfigQo, codeRuleConfig);
        codeRuleConfigMapper.updateById(codeRuleConfig);
    }

    @Transactional
    @Override
    public void removeByIds(Set<Long> idList) {
        if (idList.isEmpty()) return;
        codeRuleConfigMapper.deleteByIds(idList);
    }

    @Override
    @Lock4j(keys = {"#ruleKey"})
    public String generatedCode(String ruleKey) {
        CodeRuleConfig codeRuleConfig = codeRuleConfigMapper.selectOne(CodeRuleConfig::getRuleKey, ruleKey);
        if (codeRuleConfig == null) {
            throw new ServiceException(CommonError.DATA_NOT_EXISTS.getMsg());
        }
        return doGeneratedCode(codeRuleConfig);
    }


    /**
     * 生成编码
     * @param config 配置
     * @return String
     */
    private String doGeneratedCode(CodeRuleConfig config) {
        StringBuilder codeBuilder = new StringBuilder();
        if (config.getPrefix() != null) {
            codeBuilder.append(config.getPrefix());
        }

        // 日期格式
        if (ObjectUtil.isAllNotEmpty(config.getDateFormat())) {
            SimpleDateFormat sdf = new SimpleDateFormat(config.getDateFormat());
            setDelimiter(codeBuilder, config);
            codeBuilder.append(sdf.format(new Date()));
        }

        //生成序号
        String seqPart = switch (config.getSeqType()) {
            case 1 -> getIncrementalSequence(config);
            case 2 -> System.currentTimeMillis() + "";
            case 3 -> UUID.randomUUID().toString().replace("-", "").substring(0, config.getSeqLength());
            default -> throw new ServiceException("编码生成规则不存在");
        };

        if (seqPart.length() > config.getSeqLength()) {
            seqPart = seqPart.substring(seqPart.length() - config.getSeqLength());
        } else if (seqPart.length() < config.getSeqLength()) {
            seqPart = String.format("%" + config.getSeqLength() + "s", seqPart).replace(' ', '0');
        }

        setDelimiter(codeBuilder, config);
        codeBuilder.append(seqPart);

        // 添加后缀
        if (config.getSuffix() != null && !config.getSuffix().isEmpty()) {
            setDelimiter(codeBuilder, config);
            codeBuilder.append(config.getSuffix());
        }
        return codeBuilder.toString();
    }


    private String getIncrementalSequence(CodeRuleConfig config) {
        Long currentSeq = config.getCurrentSeq() + 1;
        config.setCurrentSeq(currentSeq);
        codeRuleConfigMapper.updateById(config);
        return currentSeq.toString();
    }


    private void setDelimiter(StringBuilder codeBuilder, CodeRuleConfig config) {
        // 添加分隔符
        if (config.getDelimiter() != null && !config.getDelimiter().isEmpty()) {
            codeBuilder.append(config.getDelimiter());
        }
    }


}

